/*
 * Copyright (C) 2001-2003  Daniel Kelly
 * Copyright (C) 2009 - 2011  Peter Pentchev
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/* program that prints IP address ranges */

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "prips.h"
#include "except.h"

typedef enum {
	FORMAT_HEX = 0,
	FORMAT_DEC = 1,
	FORMAT_DOT = 2
} AddrFormat;

#define FORMATS "hex", "dec", "dot"

const char *MAINTAINER = "Peter Pentchev <roam@ringlet.net>";
const char *VERSION =
	"\rprips 0.9.9\n\
	\rThis program comes with NO WARRANTY,\n\
	\rto the extent permitted by law.\n\
	\rYou may redistribute copies under \n\
	\rthe terms of the GNU General Public License.\n";

static void usage(char *prog);
static AddrFormat get_format(char *format);

int main(int argc, char *argv[])
{
	int ch;
	const char *argstr = "d:f:i:e:cv";
	uint32_t start = 0, end = 0, current;
	int octet[4][256];	/* Holds all of the exceptions if -e is used */
	int format = FORMAT_DOT;	/* Dotted decimal by default */
	int delimiter = 10; 	/* New line by default */
	int increment = 1;	/* Standard incrementer is one */
	char *prefix, *offset;
	
	/* flags */
	int exception_flag = 0;	/* If one, check for exclusions */
	int print_as_cidr_flag= 0;	/* If one, print range as addr/offset */

	opterr = 0;
        while ((ch = getopt(argc, argv, argstr)) != EOF) {
                switch (ch) {
                case 'c':
			print_as_cidr_flag = 1;
			break;
                case 'd':
			delimiter = atoi(optarg);
			
			if(delimiter < 0 || delimiter > 255)
			{
				fprintf(stderr, "%s: delimiter must be between 0 and 255\n", 	
					argv[0]);
				exit(1);
			}
                        break;
		case 'f':
			format = get_format(optarg);
			break;
		case 'i':
			if((increment = atoi(optarg)) < 1)
			{
				fprintf(stderr, "%s: increment must be a positive integer\n",
					 argv[0]);
				exit(1);
			}
			break;
		case 'e':
			set_exceptions(optarg, octet);
			exception_flag = 1;
			break;
		case 'v':
			printf("%s", VERSION);
			exit(0);
		case '?':
			usage(argv[0]);
			exit(1);
		}
	}

	/*************************************************************/
	/* Figure out what we have to work with.  argc - optind is   */
 	/* the number of arguments we have.  If there is one argu-   */
	/* ment, we are dealing with CIDR, or a mistake.  If ther    */
	/* are two arguments, we are dealing with start address and  */
	/* end address.  We also make sure to test if the CIDR nota- */
	/* tion is proper.                                           */
	/*************************************************************/
	
	switch(argc - optind)
	{
	case 1: /* CIDR? */
		prefix = strtok(argv[optind], "/");
		
		if((offset = strtok(NULL, "/"))) /* CIDR */
		{
			start = numberize(prefix);
			if(start == (uint32_t)-1)
			{
				fprintf(stderr, "%s: bad IP address\n", argv[0]);
				exit(1);
			}
			end = add_offset(prefix, atoi(offset));
		}
		else
		{
			usage(argv[0]);
			exit(1);
		}
		break;
	case 2: /* start address, end address */
	        start = numberize(argv[optind]);
                end = numberize(argv[optind+1]);
		if(start == (uint32_t)-1 || end == (uint32_t)-1)
		{		
			fprintf(stderr, "%s: bad IP address\n", argv[0]);
			exit(1);
		}
                break;
	default:
		usage(argv[0]);
		exit(1);
	}

	/***************************************************************/
	/* OK- at this point we have the start and end address.  If    */
	/* the start is greater than the end, we exit with an error.   */
	/* Otherwise, we start printing addresses that are not part of */
	/* the exception list, if one exists.                          */
	/***************************************************************/

	if(start > end)
	{
		fprintf(stderr, 
			"%s: start address must be smaller than end address\n",
			argv[0]);
		exit(1);
	}

	if(print_as_cidr_flag) /* print start - end as start/offset */ 
		printf("%s%c", cidrize(start, end), delimiter);
	else
	{
		for(current = start; current <= end; current += increment) 
		{	if(!exception_flag || !except(&current, octet))
			{
				switch(format)
				{
				case FORMAT_HEX:
					printf("%lx%c", (long)current, delimiter);
					break;
				case FORMAT_DEC:
					printf("%lu%c", (unsigned long)current, delimiter);
					break;
				default: 
					printf("%s%c", denumberize(current),
						delimiter);
					break;
				}
			}
		}
	}
	return(0);
} /* end main */

static void usage(char *prog)
{
	fprintf(stderr, "usage: %s [options] <start end | CIDR block>\n\
	-c		print range in CIDR notation\n\
	-d <x>		set the delimiter to the character with ASCII code 'x'\n\
			where 0 <= x <= 255 \n\
	-h		display this help message and exit \n\
	-f <x> 		set the format of addresses (hex, dec, or dot)\n\
	-i <x>		set the increment to 'x'\n\
	-e <x.x.x,x.x>	exclude a range from the output, e.g. -e ..4. will\n\
			not print 192.168.4.[0-255]\n\
	\n\
	\rReport bugs to %s\n",
			prog, MAINTAINER);
}

static AddrFormat get_format(char *format)
{
	const char *list[] = {FORMATS};
	size_t i;

	for (i = 0; i < sizeof(list) / sizeof(list[0]); i++)
		if( strcmp(format, list[i]) == 0)
			break;
	return(i);
}	
